﻿Imports System.Runtime.InteropServices
Imports Microsoft.Win32

Friend Enum IconSize As UInteger
    Large = 1
    Small = 0
End Enum

<StructLayout(LayoutKind.Sequential)> _
Friend Structure SHFILEINFO
    Public hIcon As IntPtr
    Public iIcon As IntPtr
    Public dwAttributes As UInteger
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> _
    Public szDisplayName As String
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=80)> _
    Public szTypeName As String
End Structure

Friend Class Interop
    ' Methods
    <DllImport("Shell32", CharSet:=CharSet.Auto)> _
    Friend Shared Function ExtractIconEx(<MarshalAs(UnmanagedType.LPTStr)> lpszFile As String, nIconIndex As Integer, phIconLarge As IntPtr(), phIconSmall As IntPtr(), nIcons As Integer) As Integer
    End Function

    Public Shared Function GetAssociatedIcon(file As String) As Icon
        Dim psfi As New SHFILEINFO()
        Dim ptr As IntPtr = Win32.SHGetFileInfo(file, 0, psfi, CUInt(Marshal.SizeOf(psfi)), &H101)
        If psfi.hIcon = IntPtr.Zero Then
            Return Nothing
        End If
        Return Icon.FromHandle(psfi.hIcon)
    End Function

    Public Shared Function IconFromExtension(extension As String, size As IconSize) As Icon
        Try
            If extension(0) <> "."c Then
                extension = "."c & extension
            End If
            Dim classesRoot As RegistryKey = Registry.ClassesRoot
            Dim key2 As RegistryKey = classesRoot.OpenSubKey(extension)
            key2.GetValueNames()
            Dim name As String = key2.GetValue("").ToString()
            Dim oSubKey As RegistryKey = classesRoot.OpenSubKey(name)
            Dim oDefaultIconSubKey As RegistryKey = oSubKey.OpenSubKey("DefaultIcon")
            If (oDefaultIconSubKey Is Nothing) Then
                ' some file extensions insert a '_' after the period, so look for it as well
                name = name.Substring(0, name.Length - extension.Length) & "._" & extension.TrimStart("."c)
                oSubKey = classesRoot.OpenSubKey(name)
                oDefaultIconSubKey = oSubKey.OpenSubKey("DefaultIcon")
            End If
            Dim strArray As String() = oDefaultIconSubKey.GetValue("").ToString().Split(New Char() {","c})
            If strArray(1) Is Nothing Then
                strArray(1) = "0"
            End If
            Dim phIconLarge As IntPtr() = New IntPtr(0) {}
            Dim phIconSmall As IntPtr() = New IntPtr(0) {}
            ExtractIconEx(strArray(0), Convert.ToInt16(strArray(1)), phIconLarge, phIconSmall, 1)
            Return (If((size = IconSize.Large), Icon.FromHandle(phIconLarge(0)), Icon.FromHandle(phIconSmall(0))))
        Catch generatedExceptionName As Exception
            Return Nothing
        End Try
    End Function

    Public Shared Function IconFromFile(Filename As String, Size As IconSize, Index As Integer) As Icon
        If ExtractIconEx(Filename, -1, Nothing, Nothing, 0) < Index Then
            Return Nothing
        End If
        Dim phIconSmall As IntPtr() = New IntPtr(0) {}
        If Size = IconSize.Small Then
            ExtractIconEx(Filename, 0, Nothing, phIconSmall, 1)
        Else
            ExtractIconEx(Filename, 0, phIconSmall, Nothing, 1)
        End If
        Return Icon.FromHandle(phIconSmall(0))
    End Function
End Class

Friend Class Win32
    ' Fields
    Public Const SHGFI_ICON As UInteger = &H100
    Public Const SHGFI_LARGEICON As UInteger = 0
    Public Const SHGFI_SMALLICON As UInteger = 1

    ' Methods
    <DllImport("shell32.dll")> _
    Public Shared Function SHGetFileInfo(pszPath As String, dwFileAttributes As UInteger, ByRef psfi As SHFILEINFO, cbSizeFileInfo As UInteger, uFlags As UInteger) As IntPtr
    End Function
End Class